﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using DevDefined.OAuth.Framework;
using Lightray.Hattrick.ChppLib.EntityTypes;
using Microsoft.JScript;
using System.Collections.Generic;
using System.Threading;

namespace Lightray.Hattrick.ChppLib
{
    [Guid("34AA42A0-FD30-4afa-B1F0-55A41B6095BE")]
    [ComVisible(true)]    
    public class GadgetInterface
    {
        readonly Guid storageId = new Guid("0e28507f-55b1-4515-8465-6329567c5878");

        private ComDelegate _callback;
        private DataManager _dataManager;

        public Team MyTeam { get; private set; }

        public GadgetInterface()
        {
            // needed to load refrenced dll's who are not in gac
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
        }

        public void Destroy()
        {
            System.Diagnostics.Debug.WriteLine("Stopping DataManager");
            _dataManager.Stop();
        }

        Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(asm => asm.FullName == args.Name);
        }

        #region LineupView

        public ArrayObject Starters { get; internal set; }
        public ArrayObject Subs { get; internal set; }
        public ArrayObject PenaltyShootersOrder { get; internal set; }
        public PlayerScores Captain { get; internal set; }
        public PlayerScores SetpiecesTaker { get; internal set; }
        
        public PlayerScores GetPlayerScores(Player player)
        {
            return new PlayerScores(player);
        }

        public void GenerateDefaultLineup(ArrayObject playerArray)
        {
            List<Player> playerPool = ToList(playerArray);

            Formation defaultFormation = Calculations.GetBestFormation(playerPool, Weather.Unknown, false);
            int wingbacks, defenders, wingers, playmakers, forwards;
            Calculations.GetPlayerCountPerPosition(defaultFormation, out wingbacks, out defenders, out wingers, out playmakers, out forwards);

            // TODO: take weather into account
            var starters = Calculations.GetLineup(playerPool, Weather.Unknown, false, true, wingbacks, defenders, wingers, playmakers, forwards);
            var subs = Calculations.GetLineup(playerPool, Weather.Unknown, false, false, 0, 1, 1, 1, 1);
            var penaltyShooters = Calculations.GetPenaltyShootoutOrder(starters);
            var captain = Calculations.GetCaptain(starters);
            var setpiecesTaker = Calculations.GetSetpiecesTaker(starters);

            this.Starters = ToArray(starters);
            this.Subs = ToArray(subs);
            this.PenaltyShootersOrder = ToArray(penaltyShooters);
            this.Captain = captain;
            this.SetpiecesTaker = setpiecesTaker;                        
        }

        public void GenerateLineup(ArrayObject playerArray, int wingbacks, int defenders, int wingers, int playmakers, int forwards)
        {
            List<Player> playerPool = ToList(playerArray);

            // TODO: take weather into account
            var starters = Calculations.GetLineup(playerPool, Weather.Unknown, false, true, wingbacks, defenders, wingers, playmakers, forwards);
            var subs = Calculations.GetLineup(playerPool, Weather.Unknown, false, false, 0, 1, 1, 1, 1);
            var penaltyShooters = Calculations.GetPenaltyShootoutOrder(starters);
            var captain = Calculations.GetCaptain(starters);
            var setpiecesTaker = Calculations.GetSetpiecesTaker(starters);

            this.Starters = ToArray(starters);
            this.Subs = ToArray(subs);
            this.PenaltyShootersOrder = ToArray(penaltyShooters);
            this.Captain = captain;
            this.SetpiecesTaker = setpiecesTaker;
        }
        
        #endregion

        public void StartDataManager(object onNewDataCallback)
        {
            //Thread.Sleep(5000);
            _callback = new ComDelegate(onNewDataCallback);
            _dataManager = new DataManager(storageId, getStoragePath(), readTokenFromFile(), readSecretFromFile());
            _dataManager.NewDataArrived += new EventHandler<DataManagerEventArgs>(dataManager_OnNewData);
            _dataManager.StartDataManager();
        }

        private void dataManager_OnNewData(object sender, DataManagerEventArgs e)
        {
            _callback.Invoke(e.MyTeam, getHattrickTime(e));
        }

        private long getHattrickTime(DataManagerEventArgs e)
        {
             /* Makes sure:
              * A. The data is valid for JScript (ie. in millliseconds format since 1970)
              * B. if no time was scraped, returnes estimated time using previously saved HattrickOffset + DateTime.Now */

            if (e.HattrickTime.Year < 2011)
                return ToMillisecondsSince1970(DateTime.Now + e.OffsetFromHattrickTime);
            else
                return ToMillisecondsSince1970(e.HattrickTime);            
        }

        public BrowserWindow GetUndockedWindow(object gadgetWindow)
        {
            return new BrowserWindow(this, gadgetWindow);
        }

        #region Conversion
        private static List<Player> ToList(ArrayObject playerArray)
        {
            List<Player> playerList = new List<Player>();
            for (int i = 0; i < (int)playerArray.length; i++)
                playerList.Add(playerArray[i] as Player);
            return playerList;
        }

        public ArrayObject ToArray(object o)
        {
            IEnumerable<Player> type1 = o as IEnumerable<Player>;
            if (type1 != null)
                return GlobalObject.Array.ConstructArray(type1.ToArray());

            IEnumerable<Team> type2 = o as IEnumerable<Team>;
            if (type2 != null)
                return GlobalObject.Array.ConstructArray(type2.ToArray());

            IEnumerable<PlayerScores> type3 = o as IEnumerable<PlayerScores>;
            if (type3 != null)
                return GlobalObject.Array.ConstructArray(type3.ToArray());

            return null;
        }

        public string UriToString(Uri uri)
        {
            return uri.AbsoluteUri;
        }

        public long ToMillisecondsSince1970(DateTime dt)
        {
            return (long)(dt - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
        }

        public string CountryCodeToIso3166(int code)
        {
            return Utility.CountryAsIso3166((Country)code);
        }

        public string CountryCodeToName(int code)
        {
            return ((Country)code).ToString();
        }

        public string SkillValueToName(int value)
        {
            return ((SkillLevel)value).ToString();
        }

        public string GetSkillDescription(int skillLevel)
        {
            return ((SkillLevel)skillLevel).ToString();
        }
        #endregion

        public string AssemblyLocation()
        {            
            return Assembly.GetExecutingAssembly().Location;
        }
  
        public void MsgBox(string msg)
        {
            MessageBox.Show(msg);
        }

        private IToken readTokenFromFile()
        {
            string tokenPath = Path.Combine(Path.GetDirectoryName(AssemblyLocation()), "mytoken.bin");
            BinaryFormatter bf = new BinaryFormatter();
            return bf.Deserialize(File.OpenRead(tokenPath)) as IToken;
        }

        private string readSecretFromFile()
        {
            string secretPath = Path.Combine(Path.GetDirectoryName(AssemblyLocation()), "secret.txt");
            return File.ReadAllText(secretPath);
        }

        public string getStoragePath()
        {
            return Path.Combine(Path.GetDirectoryName(AssemblyLocation()), "storages");
        }
      
    }
}
